﻿unit dwBase;

interface

uses
     //第三方
     SynCommons{用于解析JSON},
     //JsonDataObjects,


     //求MD5
     IdHashMessageDigest,IdGlobal, IdHash,

     //AES加密解密
     FlyUtils.AES,FlyUtils.CnXXX.Common,


     //系统单元
     Buttons,
     AnsiStrings,
     RegularExpressions,    //正则表达式

     HTTPApp, Dialogs, ComCtrls,Math,DateUtils,typinfo,Variants,
     Windows, Messages, SysUtils, Classes, Controls, Forms,Graphics,
     StdCtrls, ExtCtrls, StrUtils, Grids,Types,
     IniFiles,   Menus,  ShellAPI, FileCtrl ;

//Bool型转字符串：true/false
function  dwBoolToStr(AVal:Boolean):string;

//转换空格
function  dwConvertStr(AStr:String):String;

//Escape编码
function  dwEscape(const StrToEscape:string):String;

//
function  dwGetText(AText:string;ALen:integer):string;

//处理长字符
function  dwLongStr(AText:String):String;

//将PHP中的日期转换为Delphi的日期
function  dwPHPToDate(ADate:Integer):TDateTime;
function  dwDateToPHPDate(ADate:TDateTime):Integer;

//处理Caption中的特殊字符
function  dwProcessCaption(AStr:String):String;

//重排子控件
procedure dwRealignChildren(ACtrl:TWinControl;AHorz:Boolean;ASize:Integer);

//重排Panel中的子控件
procedure dwRealignPanel(APanel:TPanel;AHorz:Boolean);

//设置LTWH
function  dwSetCompLTWH(AComponent:TComponent;ALeft,ATop,AWidth,AHeight:Integer):Integer;

//设置窗体高度，以解决当窗体高度大于屏幕分辨率高度时，无法设置当前窗体高度的问题
function  dwSetHeight(AControl:TControl;AHeight:Integer):Integer;

//设置默认选中的菜单项，如：dwSetMenuDefault(MainMenu,'1-0-2');注：序号从0开始，每层之间用-隔开
function dwSetMenuDefault(AMenu:TMainMenu;ADefault:String):Integer;

//常用版ShowMessage
procedure dwShowMessage(AMsg:String;AForm:TForm);

//定制版ShowMessage, 可以定制标题， 按钮名称等
procedure dwShowMsg(AMsg,ACaption,AButtonCaption:String;AForm:TForm);

//MessageDlg
procedure dwMessageDlg(AMsg,ACaption,confirmButtonCaption,cancelButtonCaption,AMethedName:String;AForm:TForm);

//Escape解码
function  dwUnescape(S: string): string;

//将类似“%u4E2D”转成中文
function  dwUnicodeToChinese(inputstr: string): string;
function  dwISO8859ToChinese(AInput:String):string;


//Cookie操作
function  dwSetCookie(AForm:TForm;AName,AValue:String;AExpireHours:Double):Integer;  //写cookie
//function  dwPreGetCookie(AForm:TForm;AName,ANull:String):Integer;                    //预读cookie
function  dwGetCookie(AForm:TForm;AName:String):String;                              //读cookie

//输入
procedure dwInputQuery(AMsg,ACaption,ADefault,confirmButtonCaption,cancelButtonCaption,AMethedName:String;AForm:TForm);


//打开新页面
function dwOpenUrl(AForm:TForm;AUrl,Params:String):Integer;

//从控件的hint中读写值
function dwGetProp(ACtrl:TControl;AAttr:String):String;
function dwSetProp(ACtrl:TControl;AAttr,AValue:String):Integer;

//计算MD5
function dwGetMD5(AStr:String):string;

//处理ZXing扫描
function dwSetZXing(ACtrl:TControl;ACameraID:Integer):Integer;

//取得DLL名称
function dwGetDllName: string;

//执行一段JS代码，注意需要以分号结束
function dwRunJS(AJS:String;AForm:TForm):Boolean;

//快速IF
function dwIIF(ABool:Boolean;AYes,ANo:string):string;

//计算TimeLine的高度(参考)
function dwGetTimeLineHeight(APageControl:TPageControl):Integer;

//<转义可能出错的字符
function  dwChangeChar(AText:String):String;

//弹出窗体
function  dwShowModal(AForm,ASWForm:TForm):Integer;
function  dwCloseForm(AForm,ASWForm:TForm):Integer;

//计算手机可用高度
function  dwGetMobileAvailHeight(AForm:TForm):Integer;

//检查当前字符串是否JSON合法
function    dwStrIsJson(AText:String):Boolean;

//设置当前应用为移动应用，根据参数自动设置窗体Width,Height
//ADefaultWidth,ADefaultHeight为电脑浏览时的默认大小，一般建议为414/736(iPhone6/7/8 plus)
//如果有一项为0，则为当前屏幕大小（只设置宽度，不设置高度）
function    dwSetMobileMode(AForm:TForm;ADefaultWidth,ADefaultHeight:Integer):Integer;

//设置当前应用为桌面应用，根据参数自动设置窗体Width,Height
function    dwSetPCMode(AForm:TForm):Integer;


//加密函数
function dwAESDecrypt(StrHex, Key: string): string;
function dwAESEncrypt(Value, Key: string): string;

//取URL参数相关属性值
//例如:http://127.0.0.1/GetActivityinformation?language=Chinese&name=westwind
//用法:
//    sLang := dwGetParamValue(dwGetProp(Self,'params'),'language'); //获取语言
//    sName := dwGetParamValue(dwGetProp(Self,'params'),'name');    //获取name
function dwGetParamValue(QueryStr,Param_Name : string) : string;   //正则表达，获取URL中参数


//显示自动消失的消息框, AMessage 为消息内容, AType为消息类型:normal/success/warngin/error
procedure dwMessage(AMessage,AType:String;AForm:TForm);

//根据owner是否为TForm1, 来增加前缀，主要用于区分多个Form中的同名控件
function  dwPrefix(ACtrl:TComponent):String;

//StringGrid按列排序
procedure dwGridQuickSort(Grid: TStringGrid; ACol: Integer; Order: Boolean ; AIsNum: Boolean);

//StringGrid按列筛选,AFilter 为类似 "filter":["襄阳","沈阳"]
procedure dwGridQuickFilter(Grid:TStringGrid;ACol:Integer;AFilter:String);

procedure dwGridSaveCells(Grid:TStringGrid;AForce:Boolean);
procedure dwGridRestoreCells(Grid:TStringGrid);

implementation  //==================================================================================

procedure dwGridRestoreCells(Grid:TStringGrid);
var
    sHint   : String;
    joHint  : Variant;
    joRow   : Variant;
    iR,iC   : Integer;
begin
     //根据Hint生成JSON
     sHint     := Grid.Hint;
     joHint    := _json('{}');
     if dwStrIsJson(sHint) then begin
        joHint    := _json(sHint);
     end;

     //
     if joHint.Exists('__cells') then begin
        for iR := 1 to Grid.RowCount-1 do begin
            for iC := 0 to Grid.ColCount-1 do begin
                Grid.Cells[iC,iR]   := joHint.__cells._(iR-1)._(iC);
            end;
        end;
     end;

end;

procedure dwGridSaveCells(Grid:TStringGrid;AForce:Boolean);
var
    sHint   : String;
    joHint  : Variant;
    joRow   : Variant;
    iR,iC   : Integer;
begin
     //根据Hint生成JSON
     sHint     := Grid.Hint;
     joHint    := _json('{}');
     if dwStrIsJson(sHint) then begin
        joHint    := _json(sHint);
     end;

     //
     if AForce OR (not joHint.Exists('__cells')) then begin
        joHint.__cells  := _json('[]');
        for iR := 1 to Grid.RowCount-1 do begin
            joRow   := _json('[]');
            for iC := 0 to Grid.ColCount-1 do begin
                joRow.Add(Grid.Cells[iC,iR]);
            end;
            joHint.__cells.Add(joRow);
        end;
        //
        Grid.Hint   := joHint;
     end;

end;

procedure dwGridQuickFilter(Grid:TStringGrid;ACol:Integer;AFilter:String);
    function InFilter(Grid:TStringGrid;ACol,ARow:Integer;AFilter:Variant):Boolean;
    var
        ii  : Integer;
        ss  : string;
    begin
        Result  := False;
        for ii := 0 to AFilter.filter._Count-1 do begin
            ss  := AFilter.filter._(ii);
            if Pos(ss,Grid.Cells[ACol,ARow])>0 then begin
                Result  := True;
                break;
            end;
        end;
    end;
    procedure CopyRowFromNext(Grid:TStringGrid;ARow:Integer);
    var
        iiC : Integer;
    begin
        for iiC := 0 to Grid.ColCount-1 do begin
            if ARow = Grid.RowCount - 1 then begin
                Grid.Cells[iiC,ARow]    := '';
            end else begin
                Grid.Cells[iiC,ARow]    := Grid.Cells[iiC,ARow+1];
            end;
        end;
    end;
var
    iR,iR1,iC   : Integer;
    joFilter    : Variant;
    iCount      : Integer;
begin
    if dwStrIsJson(AFilter) then begin
        //
        dwGridSaveCells(Grid,False);
        //
        dwGridRestoreCells(Grid);

        //
        joFilter    := _json(AFilter);

        iR := 1;
        iCount  := Grid.RowCount-1;
        while iR <= iCount do begin
            //
            if InFilter(Grid,ACol,iR,joFilter) then begin
                Inc(iR);
            end else begin
                for iR1 := iR to Grid.RowCount-1 do begin
                    CopyRowFromNext(Grid,iR1);
                end;
                Dec(iCount);
            end;
            //break;
        end;
    end else begin
        dwGridRestoreCells(Grid);
    end;
end;


procedure dwGridQuickSort(Grid: TStringGrid; ACol: Integer; Order: Boolean ; AIsNum: Boolean);
    procedure dwSwapRow(Grid:TStringGrid;ARow0,ARow1:Integer);
    begin
        var sTmp : String;
        for var iCol := 0 to Grid.ColCount-1 do begin
            sTmp    := Grid.Cells[iCol,ARow0];
            Grid.Cells[iCol,ARow0]  := Grid.Cells[iCol,ARow1];
            Grid.Cells[iCol,ARow1]  := sTmp;
        end;
    end;
begin
    for var I := 1 to Grid.RowCount-1 do begin
        for var J := 1 to Grid.RowCount-1 do begin
            if Order then begin
                if AIsNum then begin
                    if StrToFloatDef(Grid.Cells[ACol,J],0) > StrToFloatDef(Grid.Cells[ACol,I],0) then begin
                        dwSwapRow(Grid,I,J);
                    end;
                end else begin
                    if Grid.Cells[ACol,J] > Grid.Cells[ACol,I] then begin
                        dwSwapRow(Grid,I,J);
                    end;
                end;
            end else begin
                if AIsNum then begin
                    if StrToFloatDef(Grid.Cells[ACol,J],0) < StrToFloatDef(Grid.Cells[ACol,I],0) then begin
                        dwSwapRow(Grid,I,J);
                    end;
                end else begin
                    if Grid.Cells[ACol,J] < Grid.Cells[ACol,I] then begin
                        dwSwapRow(Grid,I,J);
                    end;
                end;
            end;
        end;
    end;
end;



//根据owner是否为TForm1, 来增加前缀，主要用于区分多个Form中的同名控件
function  dwPrefix(ACtrl:TComponent):String;
begin

     //默认为空
     Result    := '';
     //
     if lowerCase(ACtrl.Owner.ClassName) <> 'tform1' then begin
          Result    := ACtrl.Owner.Name+'__';
     end;
end;


//显示自动消失的消息框, AMessage 为消息内容, AType为消息类型:info/success/warning/error
procedure dwMessage(AMessage,AType:String;AForm:TForm);
begin
    if LowerCase(AType) = 'success' then begin
        dwRunJs('this.$message({  message: '''+AMessage+''', type: ''success'' });',AForm);
    end else if LowerCase(AType) = 'warning' then begin
        dwRunJs('this.$message({  message: '''+AMessage+''', type: ''warning'' });',AForm);
    end else if LowerCase(AType) = 'error' then begin
        dwRunJs('this.$message({  message: '''+AMessage+''', type: ''error'' });',AForm);
    end else begin
        dwRunJs('this.$message({  message: '''+AMessage+'''});',AForm);
    end;
end;

function dwGetParamValue(QueryStr,Param_Name : string) : string;   //正则表达，获取page参数
var
    Reg    : TRegEx;
    Match  : TMatch;
begin
    Result := '';
    Match := Reg.Match(QueryStr,'(?<=' + Param_Name + '=)[^&]*');
    if Match.Success then begin
        Result := Match.Value;
    end;
end;



function dwAESDecrypt(StrHex, Key: string): string;
begin
  //解密
  Result    := HexToStrByEncoding(StrHex);
  Result    := AESDecryptStr(DecodeBase64Bytes(Result), Key, TEncoding.UTF8, TEncoding.ANSI, kb128,
    'deweb', pmPKCS5or7RandomPadding, False, rlCRLF, rlCRLF);
end;

function dwAESEncrypt(Value, Key: string): string;
begin
  //加密
  Result := StrToHexByEncoding(EncodeBase64Bytes(AESEncryptStr(Value, Key, TEncoding.UTF8, TEncoding.ANSI, kb128,
    'deweb',pmPKCS5or7RandomPadding, False, rlCRLF, rlCRLF)));
end;


function dwGetMD5(AStr:String):string;
var
     oMD5      : TIdHashMessageDigest5;
begin
     oMD5      := TIdHashMessageDigest5.Create;
     Result    := LowerCase(oMD5.HashStringAsHex(AStr));
     oMD5.Free;
end;

function    dwSetMobileMode(AForm:TForm;ADefaultWidth,ADefaultHeight:Integer):Integer;
var
    sInit   : String;
    //
    iOS     : Integer;
    iOrient : Integer;
    iW,iH   : Integer;
    iCltW   : Integer;
    iCltH   : Integer;
    //
    iMin    : Integer;
    iMax    : Integer;
    iMaxT   : Integer;
    iMinT   : Integer;
    iMinI   : Integer;
    iMaxI   : Integer;
    iMinI0  : Integer;
    iMaxI0  : Integer;
    bVert   : Boolean;
begin
    //
    iOs     := StrToIntDef(dwGetProp(AForm,'os'),0);            //分别对应0:未知/1:PC/2:Android/3:iPhone/4:Tablet
    iOrient := StrToIntDef(dwGetProp(AForm,'orientation'),0);   //

    //虚拟分辨率
    iW      := StrToIntDef(dwGetProp(AForm,'screenwidth'),0);
    iH      := StrToIntDef(dwGetProp(AForm,'screenheight'),0);

    //
    iCltW   := StrToIntDef(dwGetProp(AForm,'clientwidth'),0);
    iCltH   := StrToIntDef(dwGetProp(AForm,'clientheight'),0);



    if (iW>600)and(iH>600) then begin   //如果是大屏（电脑或平板）
        //如果未指定宽,高,则为屏幕宽(不设置高);否则,设置为默认值
        if ADefaultWidth*ADefaultHeight = 0 then begin
            AForm.Width     := iCltW;
        end else begin
            AForm.Width     := ADefaultWidth;
            AForm.Height    := ADefaultHeight;
        end;
    end else begin
        //检查是否已初始化
        sInit   := dwGetProp(AForm,'_inited');

        //如果未初始化,则先设置初始化标志,再计算宽度; 如果已初始化,则直接设置为Client宽高
        if sInit = '' then begin
            //
            dwSetProp(AForm,'_inited','true');

            //
            bVert   := (iOrient = 0) or (iOrient = 180);

            if bVert then begin
                //竖屏

                //
                AForm.Width     := iW;
                AForm.Height    := Ceil(iW*iCltH/iCltW);
            end else begin
                //横屏

                iMax    := Max(iW,iH);
                iMin    := Min(iW,iH);
                //
                AForm.Width     := iMax;
                AForm.Height    := Ceil(iMax*iCltH/iCltW);
            end;
        end else begin
            //
            iCltW   := StrToIntDef(dwGetProp(AForm,'clientwidth'),0);      //
            iCltH   := StrToIntDef(dwGetProp(AForm,'clientheight'),0);

            //
            AForm.Width     := iCltW;
            AForm.Height    := iCltH;
        end;
    end;

end;

function    dwSetPCMode(AForm:TForm):Integer;
var
    iW,iH   : Integer;
begin
    //分辨率
    iW      := StrToIntDef(dwGetProp(AForm,'innerwidth'),0);
    iH      := StrToIntDef(dwGetProp(AForm,'innerheight'),0);

    if (iW>0)and(iH>0) then begin
        AForm.Left      := 0;
        AForm.Top       := 0;
        AForm.Width     := iW;
        AForm.Height    := iH;
    end;

end;

function    dwStrIsJson(AText:String):Boolean;
begin
    //D自带单元的写法. uses system.json
    //Result  := System.Json.TJSONObject.ParseJSONValue(Trim(AText)) <> nil;

    //mormot的写法
    Result  := _json(AText) <> unassigned;
end;

//计算手机可用高度
function  dwGetMobileAvailHeight(AForm:TForm):Integer;
var
     iX,iY     : Integer;
     iTrueH    : Integer;
     iInnerH   : Integer;
     iTrueW    : Integer;
     iInnerW   : Integer;
begin
     iX        := StrToIntDef(dwGetProp(AForm,'screenwidth'),360);
     iY        := StrToIntDef(dwGetProp(AForm,'screenheight'),720);
     //
     iTrueW    := StrToIntDef(dwGetProp(AForm,'truewidth'),iX);
     iTrueH    := StrToIntDef(dwGetProp(AForm,'trueheight'),iY);
     iInnerW   := StrToIntDef(dwGetProp(AForm,'innerwidth'),iX);
     iInnerH   := StrToIntDef(dwGetProp(AForm,'innerheight'),iY);

     //
     Result    := Ceil(iInnerH*iY/iTrueH*iTrueW/iInnerW);

end;


//弹出窗体
function  dwShowModal(AForm,ASWForm:TForm):Integer;
var
    sClass  : String;
    sPrefix : String;
    sJS     : string;
    iCtrl   : Integer;
begin
    for iCtrl :=0 to AForm.ControlCount-1 do begin
        sClass    := LowerCase(AForm.Controls[iCtrl].ClassName);
        //
        if sClass = LowerCase(ASWForm.ClassName) then begin
            sPrefix := 'this.'+AForm.Controls[iCtrl].Name+'__';
            sJS     := sPrefix+'cap="'+ASWForm.Caption+'";'
                    //+sPrefix+'wid="'+IntToStr(ASWForm.Width)+'px";'
                    //+sPrefix+'hei="'+IntToStr(ASWForm.Height)+'px";'
                    +sPrefix+'vis=true;';
            dwRunJS(sJS,AForm);
            //
            break;
        end;
    end;
    Result    := 0;
end;


function  dwCloseForm(AForm,ASWForm:TForm):Integer;
var
     sClass    : String;
     iCtrl     : Integer;
begin
     for iCtrl :=0 to AForm.ControlCount-1 do begin
          sClass    := LowerCase(AForm.Controls[iCtrl].ClassName);
          //
          if sClass = LowerCase(ASWForm.ClassName) then begin
               dwRunJS('this.'+AForm.Controls[iCtrl].Name+'__vis=false;',AForm);
               //
               break;
          end;
     end;
     Result    := 0;
end;

//<转义可能出错的字符
function  dwChangeChar(AText:String):String;
begin
     AText     := StringReplace(AText,'\"','[!__!]',[rfReplaceAll]);
     AText     := StringReplace(AText,'"','\"',[rfReplaceAll]);
     AText     := StringReplace(AText,'[!__!]','\"',[rfReplaceAll]);

     AText     := StringReplace(AText,'\>','[!__!]',[rfReplaceAll]);
     AText     := StringReplace(AText,'>','\>',[rfReplaceAll]);
     AText     := StringReplace(AText,'[!__!]','\>',[rfReplaceAll]);

     AText     := StringReplace(AText,'\<','[!__!]',[rfReplaceAll]);
     AText     := StringReplace(AText,'<','\<',[rfReplaceAll]);
     AText     := StringReplace(AText,'[!__!]','\<',[rfReplaceAll]);
     //>
     //
     Result    := AText;
end;


//计算TimeLine的高度
function dwGetTimeLineHeight(APageControl:TPageControl):Integer;
var
     iTab      : Integer;
     iTabW     : Integer;
     iCtrl     : Integer;
     iLns      : Integer;
     iRow      : Integer;
     //
     oTab      : TTabSheet;
     oLabel    : TLabel;
     oMemo     : TMemo;
     oForm     : TForm;

begin
     oForm     := TForm(APageControl.Owner);
     oForm.Canvas.Font.Size   := 10;
     //
     Result    := 0;
     for iTab := 0 to APageControl.PageCount-1 do begin
          //日期高度
          if iTab = 0 then begin
               Result    := Result + 38;
          end else begin
               Result    := Result + 45;
          end;
          //标题高度
          Result    := Result + 80;
          //
          oTab      := APageControl.Pages[iTab];
          iTabW     := oTab.Width;

          //
          for iCtrl := 0 to oTab.ControlCount-1 do begin
               if oTab.Controls[iCtrl].ClassName = 'TLabel' then begin
                    oLabel    := TLabel(oTab.Controls[iCtrl]);
                    iLns      := Ceil(oForm.Canvas.TextWidth(oLabel.Caption) / (iTabW-70));
                    //
                    Result    := Result + iLns*11+(iLns-1)*8 + 24;
               end else if oTab.Controls[iCtrl].ClassName = 'TMemo' then begin
                    oMemo     := TMemo(oTab.Controls[iCtrl]);
                    for iRow := 0 to oMemo.Lines.Count-1 do begin
                         iLns      := Ceil(oForm.Canvas.TextWidth(oMemo.Lines[iRow]) / (iTabW-70));
                         //
                         Result    := Result + iLns*11+(iLns-1)*8 + 24;
                    end;
               end;
          end;

          //
          Result    := Result + 15;
     end;
     //
     Result    := Result + 15;

end;

function dwIIF(ABool:Boolean;AYes,ANo:string):string;
begin
     if ABool then begin
          Result    := AYes;
     end else begin
          Result    := ANo;
     end;
end;


function dwRunJS(AJS:String;AForm:TForm):Boolean;
begin
     AForm.HelpFile := AForm.HelpFile + AJS;
     //
     Result    := True;

end;

function dwGetDllName: string;
var
     sModule   : string;
begin
     SetLength(sModule, 255);
     //取得Dll自身路径
     GetModuleFileName(HInstance, PChar(sModule), Length(sModule));
     //去除路径
     while Pos('\',sModule)>0 do begin
          Delete(sModule,1,Pos('\',sModule));
     end;
     //去除.dll
     if Pos('.',sModule)>0 then begin
          sModule     := Copy(sModule,1,Pos('.',sModule)-1);
     end;

     //
     Result := PChar(sModule);
end;



function StrSubCount(const Source, Sub: string): integer;
var
     Buf : string;
     i : integer;
     Len : integer;
begin
     Result := 0;
     Buf:=Source;
     i := Pos(Sub, Buf);
     Len := Length(Sub);
     while i <> 0 do begin
          Inc(Result);
          Delete(Buf, 1, i + Len -1);
          i:=Pos(Sub,Buf);
     end;
end;

function  dwISO8859ToChinese(AInput:String):string;
var
     iSource   : Integer;
     iDecode   : Integer;
     sDecode   : String;
begin
     sDecode   := TEncoding.GetEncoding(936).GetString(TEncoding.GetEncoding('iso-8859-1').GetBytes(AInput));
     //
     iSource   := StrSubCount(AInput,'?');
     iDecode   := StrSubCount(sDecode,'?');
     //
     if iSource<iDecode then begin
          Result    := AInput;
     end else begin
          Result    := sDecode;
     end;
end;


//处理ZXing扫描
function dwSetZXing(ACtrl:TControl;ACameraID:Integer):Integer;
var
     sJS       : string;
const
     _JS       : string = ''
(*
                    +#13'var easyUTF8 = function(gbk){'
                    +#13'    if(!gbk){return '''';}'
                    +#13'    var utf8 = [];'
                    +#13'    for(var i=0;i<gbk.length;i++){'
                    +#13'        var s_str = gbk.charAt(i);'
                    +#13'        if(!(/^%u/i.test(escape(s_str)))){utf8.push(s_str);continue;}'
                    +#13'        var s_char = gbk.charCodeAt(i);'
                    +#13'        var b_char = s_char.toString(2).split('''');'
                    +#13'        var c_char = (b_char.length==15)?[0].concat(b_char):b_char;'
                    +#13'        var a_b =[];'
                    +#13'        a_b[0] = ''1110''+c_char.splice(0,4).join('''');'
                    +#13'        a_b[1] = ''10''+c_char.splice(0,6).join('''');'
                    +#13'        a_b[2] = ''10''+c_char.splice(0,6).join('''');'
                    +#13'        for(var n=0;n<a_b.length;n++){'
                    +#13'            utf8.push(''%''+parseInt(a_b[n],2).toString(16).toUpperCase());'
                    +#13'        }'
                    +#13'    }'
                    +#13'    return utf8.join('''');'
                    +#13'};'
*)
                    +#13'let selectedDeviceId=%d;'
                    +#13'const codeReader = new ZXing.BrowserMultiFormatReader();'
				+#13'codeReader.reset();'
				+#13'codeReader.decodeFromVideoDevice(selectedDeviceId, ''%s'', (result, err) => {'
				+#13'	if (result) {'
				//+#13'		alert(result);'
				//+#13'		alert(decodeURI((result)));'
 				+#13'		axios.get(''{"m":"event","i":%d,"c":"%s","name":"%s","v":"''+(escape(result))+''"}'')'
 				+#13'		.then(resp =>{this.procResp(resp.data);});'
				+#13'	}'
				+#13'})'
                    ;
begin
     sJS  := Format(_JS,[ACameraID,ACtrl.Name,TForm(ACtrl.Owner).Handle,ACtrl.Name,'onenddock']);
     //
     TForm(ACtrl.Owner).HelpFile   := TForm(ACtrl.Owner).HelpFile + sJS;
     //
     Result    := 0;
end;



function dwOpenUrl(AForm:TForm;AUrl,Params:String):Integer;
var
     sCode     : string;
begin
     sCode     := 'this.ToWebsite("'+AUrl+'","'+Params+'");';
     //
     AForm.HelpFile := AForm.HelpFile + sCode;
     //
     Result    := 0;
end;

//Cookie操作
function  dwSetCookie(AForm:TForm;AName,AValue:String;AExpireHours:Double):Integer;
var
     sCode     : string;
     sHint     : String;
     joHint    : variant;
begin
     sCode     := 'this.dwsetcookie("'+AName+'","'+AValue+'",1);';

     //
     AForm.HelpFile := AForm.HelpFile + sCode;

     //写到本地
     sHint     := AForm.Hint;
     joHint    := _json('{}');
     if dwStrIsJson(sHint) then begin
        joHint    := _json(sHint);
     end;
     //
     if not joHint.Exists('_cookies') then begin
          joHint._cookies   := _json('{}');
     end;
     joHint._cookies.Add(AName,AValue);
     AForm.Hint     := joHint;


     //
     Result    := 0;
end;

function  dwPreGetCookie(AForm:TForm;AName,ANull:String):Integer;                    //预读cookie
var
     sCode     : string;
     sHint     : String;
     joHint    : Variant;
const
     sUpload   = 'axios.get(''{"m":"event","i":%d,"c":"_cookie","name":"%s","v":"''+res+''"}'');';
          //+'.then(resp =>{'
          //+'this.procResp(resp.data);'
          //+'},resp => {'
          //+'console.log("err");'
          //+'});';


begin
     //预置cookie空值到本地
     sHint     := AForm.Hint;
     joHint    := _json('{}');
     if dwStrIsJson(sHint) then begin
        joHint    := _json(sHint);
     end;
     //
     if not joHint.Contains('_cookies') then begin
          joHint._cookies   := _json('{}');
     end;
     joHint._cookies.Delete(AName);
     AForm.Hint     := joHint;

     //
     //sCode     := 'var reg=new RegExp("(^| )"+'+AName+'+"=([^;]*)(;|$)");';
     //sCode     := sCode + 'var arr,res;if(arr=document.cookie.match(reg)) res = unescape(arr[2]); else res="'+ANull+'";console.log(res);';
     //sCode     := sCode + Format(sUpload,[AForm.Handle,AName]);

     //
     sCode     := 'var res=this.dwgetcookie("'+AName+'"); ';
     sCode     := sCode + Format(sUpload,[AForm.Handle,AName]);
     //
     AForm.HelpFile := AForm.HelpFile + sCode;

     //


     //
     Result    := 0;
end;


function dwUnicodeToChinese(inputstr: string): string;   //将类似“%u4E2D”转成中文
var
     index: Integer;
     temp, top, last: string;
begin
     index := 1;
     while index >= 0 do begin
          index := Pos('%u', inputstr) - 1;
          if index < 0 then begin
               last := inputstr;
               Result := Result + last;
               Exit;
          end;
          top := Copy(inputstr, 1, index); // 取出 编码字符前的 非 unic 编码的字符，如数字
          temp := Copy(inputstr, index + 1, 6); // 取出编码，包括 \u,如\u4e3f
          Delete(temp, 1, 2);
          Delete(inputstr, 1, index + 6);
          Result := Result + top + WideChar(StrToInt('$' + temp));
     end;
end;

function  dwGetCookie(AForm:TForm;AName:String):String;                             //读cookie
var
    sHint     : String;
    joHint    : Variant;
begin
    //
    sHint     := AForm.Hint;
    joHint    := _json('{}');
    if dwStrIsJson(sHint) then begin
        joHint    := _json(sHint);
    end;
    //
    Result    := '';
    if joHint.Exists('_cookies') then begin
        if joHint._cookies.Exists(AName) then begin
            Result    := dwUnicodeToChinese(HttpDecode(joHint._cookies._(AName)));
        end;
    end;
end;

procedure dwRealignPanel(APanel:TPanel;AHorz:Boolean);
var
     iCtrl     : Integer;
     oCtrl     : TControl;
     oCtrl0    : TControl;
begin
     //
     if APanel.ControlCount<=1 then begin
          Exit;
     end;

     //取得第一个控件, 以检测当前状态
     oCtrl0    := APanel.Controls[0];

     if AHorz then begin
          //水平排列的情况
          if (oCtrl0.Align = alLeft) and (oCtrl0.Width = (APanel.Width-2*APanel.BorderWidth) div APanel.ControlCount) then begin
               //已经水平排列,
          end else begin
               APanel.Height  := APanel.BorderWidth*2+oCtrl0.Height;
               //
               for iCtrl := 0 to APanel.ControlCount-2 do begin
                    oCtrl     := APanel.Controls[iCtrl];
                    //
                    oCtrl.Align    := alLeft;
                    oCtrl.Width    := (APanel.Width-2*APanel.BorderWidth) div APanel.ControlCount;
                    oCtrl.Left     := 9000+iCtrl;
               end;
               //最后一个alClient
               oCtrl     := APanel.Controls[APanel.ControlCount-1];
               oCtrl.Align    := alClient;
          end;
     end else begin
          //垂直排列的情况
          if (oCtrl0.Align = alTop) and (oCtrl0.Height = (APanel.Height-2*APanel.BorderWidth) div APanel.ControlCount) then begin
               //已经垂直排列,
          end else begin
               APanel.Height  := APanel.BorderWidth*2+oCtrl0.Height*APanel.ControlCount;
               //
               for iCtrl := 0 to APanel.ControlCount-2 do begin
                    oCtrl     := APanel.Controls[iCtrl];
                    //
                    oCtrl.Align    := alTop;
                    oCtrl.Height   := (APanel.Height-2*APanel.BorderWidth) div APanel.ControlCount;
                    oCtrl.Top      := 9000+iCtrl;
               end;
               //最后一个alClient
               oCtrl     := APanel.Controls[APanel.ControlCount-1];
               oCtrl.Align    := alClient;
          end;
     end;

end;



function dwPHPToDate(ADate:Integer):TDateTime;
var
     f1970     : TDateTime;
begin
     //PHP时间是格林威治时间1970-1-1 00:00:00到当前流逝的秒数
     f1970     := EncodeDateTime(1970, 1, 1, 8, 0, 0, 0);//StrToDateTime('1970-01-01 00:00:00');
     Result    := IncSecond(f1970,ADate);
     //Result    := ((ADate+28800)/86400+25569);
end;

function dwDateToPHPDate(ADate:TDateTime):Integer;
var
     f1970     : TDateTime;
begin
     //PHP时间是格林威治时间1970-1-1 08:00:00到当前流逝的秒数
     f1970     := EncodeDateTime(1970, 1, 1, 8, 0, 0, 0);//StrToDateTime('1970-01-01 00:00:00');
     //
     Result    := Round((ADate - f1970)*24*3600);
     //Result    := ((ADate+28800)/86400+25569);
end;

function dwSetHeight(AControl:TControl;AHeight:Integer):Integer;
var
     sHint     : String;
     joHint    : Variant;
begin
     sHint     := AControl.Hint;
     joHint    := _json('{}');
     if dwStrIsJson(sHint) then begin
        joHint    := _json(sHint);
     end;
     joHint.height  := AHeight;
     AControl.Hint  := joHint;

     //
     Result    := 0;
end;


procedure dwShowMessage(AMsg:String;AForm:TForm);
begin
     AMsg := StringReplace(AMsg,'''','\''',[rfReplaceAll]);
     dwShowMsg((AMsg),AForm.Caption,'OK',AForm);
end;

procedure dwShowMsg(AMsg,ACaption,AButtonCaption:String;AForm:TForm);
var
     sMsgCode  : string;
begin
     //处理sMsg
     AMsg := StringReplace(AMsg,#13,'\r\n',[rfReplaceAll]);
     AMsg := StringReplace(AMsg,#10,'',[rfReplaceAll]);

     //
     sMsgCode  := 'this.$alert(''%s'', ''%s'', { confirmButtonText: ''%s''});';
     sMsgCode  := Format(sMsgCode,[AMsg,ACaption,AButtonCaption]);
     AForm.HelpFile := AForm.HelpFile + sMsgCode;
end;


//MessageDlg
procedure dwMessageDlg(AMsg,ACaption,confirmButtonCaption,cancelButtonCaption,AMethedName:String;AForm:TForm);
var
     sMsgCode  : string;
const
     sConfirm  = 'axios.get(''{"m":"interaction","i":%d,"t":"%s","v":%d}'')'
          +'.then(resp =>{'
          +'this.procResp(resp.data);'
          +'},resp => {'
          +'console.log("err");'
          +'});';

begin
     sMsgCode  := 'this.$confirm(''%s'', ''%s'', {confirmButtonText: ''%s'', cancelButtonText: ''%s'', type: ''warning''})'
               +'.then(()  => {'
               //+'    this.$message({type: ''success'',message: ''删除成功!'' });'
               +Format(sConfirm,[AForm.Handle, AMethedName,1])
               +'})'
               +'.catch(() => {'
               //+'    this.$message({type: ''info'',   message: ''已取消删除''});'
               +Format(sConfirm,[AForm.Handle, AMethedName,0])
               +'});';
     sMsgCode  := Format(sMsgCode,[AMsg,ACaption,confirmButtonCaption,cancelButtonCaption]);
     AForm.HelpFile  := sMsgCode;
end;




function dwGetProp(ACtrl:TControl;AAttr:String):String;
var
     sHint     : String;
     joHint    : Variant;
begin
    //
    sHint     := ACtrl.Hint;

    //创建HINT对象, 用于生成一些额外属性
    joHint    := _json('{}');

    if dwStrIsJson(sHint) then begin
        joHint    := _json(sHint);
    end;

    //
    if joHint.Exists(AAttr) then begin
        Result    := joHint._(AAttr);
    end else begin
        Result  := '';
    end;
end;

function dwSetProp(ACtrl:TControl;AAttr,AValue:String):Integer;
var
    sHint     : String;
    joHint    : Variant;
begin
    Result    := 0;
    //
    sHint     := ACtrl.Hint;

    //创建HINT对象, 用于生成一些额外属性
    if dwStrIsJson(sHint) then begin
        joHint  := _json(sHint);
    end else begin
        joHint  := _json('{}');
    end;

    //如果当前存在该属性, 则先删除
    if joHint.Exists(AAttr) then begin
        joHint.Delete(AAttr);
    end;

    //添加属性
    joHint.Add(AAttr,AValue);

    //返回到HINT字符串
    ACtrl.Hint     := joHint;

end;


function dwEscape(const StrToEscape:string):String;
var
   i:Integer;

   w:Word;
begin
     Result:='';

     for i:=1 to Length(StrToEscape) do
     begin
          w:=Word(StrToEscape[i]);

          if w in [Ord('0')..Ord('9'),Ord('A')..Ord('Z'),Ord('a')..Ord('z')] then
             Result:=Result+Char(w)
          else if w<=255 then
               Result:=Result+'%'+IntToHex(w,2)
          else
               Result:=Result+'%u'+IntToHex(w,4);
     end;
end;

function dwUnescape(S: string): string;
var
     i0,i1     : Integer;
begin
     Result := '';
     while Length(S) > 0 do
     begin
          if S[1]<>'%' then
          begin
               Result    := Result + S[1];
               Delete(S,1,1);
          end
          else
          begin
               Delete(S,1,1);
               if S[1]='u' then
               begin
                    try
                         //Result    := Result + Chr(StrToInt('$'+Copy(S, 2, 2)))+ Chr(StrToInt('$'+Copy(S, 4, 2)));
                         i0   := StrToInt('$'+Copy(S, 2, 2));
                         i1   := StrToInt('$'+Copy(S, 4, 2));
                         Result    := Result + WideChar((i0 shl 8) or i1);
                    except
                         ShowMessage(Result);

                    end;
                    Delete(S,1,5);
               end
               else
               begin
                    try
                         Result    := Result + Chr(StrToInt('$'+Copy(S, 1, 2)));
                    except
                         ShowMessage(Result);

                    end;
                    Delete(S,1,2);
               end;
          end;
     end;
end;



procedure dwRealignChildren(ACtrl:TWinControl;AHorz:Boolean;ASize:Integer);
var
     iCount    : Integer;
     iItem     : Integer;
     iW        : Integer;
     iItemW    : Integer;
     //
     oCtrl     : TControl;
     //
     procedure _AutoSize(ooCtrl:TControl);
     begin
          if Assigned(GetPropInfo(ooCtrl.ClassInfo,'AutoSize')) then begin
               TPanel(ooCtrl).AutoSize  := False;
               TPanel(ooCtrl).AutoSize  := True;
          end;
     end;
begin
     //重排ACtrl的子控件
     //如果水平(AHorz=True), 则取所有控件等宽水平放置
     //如果垂直, 则所有控件Align=alTop


     //得到子控件数量
     iCount    := ACtrl.ControlCount;
     if iCount = 0 then begin
          Exit;
     end;


     if AHorz then begin
          //水平排列

          //先取得总宽度
          if Assigned(GetPropInfo(ACtrl.ClassInfo,'BorderWidth')) then begin
               iW   := ACtrl.Width - TPanel(ACtrl).BorderWidth;
          end else begin
               iW   := ACtrl.Width;
          end;
          iItemW    := Round(iW / iCount);

          //重新排列
          for iItem := 0 to ACtrl.ControlCount-1 do begin
               oCtrl     := ACtrl.Controls[iItem];
               //自动大小
               //_AutoSize(oCtrl);
               //
               if iItem<ACtrl.ControlCount-1 then begin
                    oCtrl.Align    := alLeft;
                    oCtrl.Width    := iItemW;
                    oCtrl.Top      := 0;
                    oCtrl.Left     := 99999;
               end else begin
                    oCtrl.Align    := alClient;
               end;

               //自动大小
               _AutoSize(oCtrl);
          end;

          //自动大小
          _AutoSize(ACtrl);
     end else begin
          //垂直排列

          //重新排列
          for iItem := 0 to ACtrl.ControlCount-1 do begin
               oCtrl     := ACtrl.Controls[iItem];
               //自动大小
               _AutoSize(oCtrl);
               //
               oCtrl.Align    := alTop;
               oCtrl.Top      := 99999;
               if ASize>0 then begin
                    oCtrl.Height   := ASize;
               end else begin
                    //自动大小
                    _AutoSize(oCtrl);
               end;
          end;

          //自动大小
          _AutoSize(ACtrl);
     end;

end;



function dwConvertStr(AStr:String):String;
begin
     //替换空格
     Result    := StringReplace(AStr,' ','&ensp;',[rfReplaceAll]);
end;

function dwProcessCaption(AStr:String):String;
begin
     //替换空格
     Result    := AStr;
     //Result    := StringReplace(Result,' ','&nbsp;',[rfReplaceAll]);
     Result    := StringReplace(Result,'"','\"',[rfReplaceAll]);
     Result    := StringReplace(Result,'''','\''',[rfReplaceAll]);
     Result    := StringReplace(Result,#13#10,'\n',[rfReplaceAll]);
     Result    := Trim(Result);
end;


function dwBoolToStr(AVal:Boolean):string;
begin
     if AVal then begin
          Result    := 'true';
     end else begin
          Result    := 'false';
     end;
end;




function dwGetText(AText:string;ALen:integer):string;
begin
     if Length(AText)<ALen then begin
          Result    := AText;
     end else begin
          //先判断要截取的字符串最后一个字节的类型
          //如果为汉字的第一个字节则减(加)一位
          if ByteType(AText,ALen) = mbLeadByte then
               ALen := ALen - 1;
          result := copy(AText,1,ALen) + '...';
     end;
end;

function dwLongStr(AText:String):String;
var
     slTmp     : TStringList;
     iItem     : Integer;
begin
     if AText = '' then begin
          Result    := AText;
     end else begin
          slTmp     := TStringList.Create;
          //AText     := StringReplace(AText,'<br/>','"'#13'+"',[rfReplaceAll]);
          //AText     := StringReplace(AText,'<br>', '"'#13'+"',[rfReplaceAll]);
          slTmp.Text     := AText;
          //
          Result    := '';
          for iItem := 0 to slTmp.Count-2 do begin
               Result    := Result + slTmp[iItem]+#13#10;
          end;
          Result    := Result + slTmp[slTmp.Count-1];
          slTmp.Destroy;
     end;
end;

function dwSetMenuDefault(AMenu:TMainMenu;ADefault:String):Integer;
var
    oItem0  : TMenuItem;
    sHint   : string;
    joHint  : Variant;
begin
     if AMenu.Items.Count>0 then begin
        //
        oItem0    := AMenu.Items[0];

        //取得HINT对象JSON
        sHint     := '{}';
        if AMenu.Items.Count>0 then begin
            sHint     := AMenu.Items[0].Hint;
        end;
        if dwStrIsJson(sHint) then begin
            joHint  := _Json(sHint);
        end else begin
            joHint  := _json('{}');
        end;

        //
        joHint.activeindex  := ADefault;
        //
        oItem0.Hint    := joHint;
        //
        Result    := 0;
    end else begin
        Result    := -1;
    end;
end;


function dwSetCompLTWH(AComponent:TComponent;ALeft,ATop,AWidth,AHeight:Integer):Integer;
begin
     AComponent.DesignInfo    := ALeft  * 10000 + ATop;
     AComponent.Tag           := AWidth * 10000 + AHeight;
     //
     Result    := 0;
end;

procedure dwInputQuery(AMsg,ACaption,ADefault,confirmButtonCaption,cancelButtonCaption,AMethedName:String;AForm:TForm);
var
     sMsgCode  : string;
begin
     sMsgCode  := 'this.input_query_caption="'+ACaption+'";'
          +'this.input_query_inputname="'+AMsg+'";'
          +'this.input_query_inputdefault="'+ADefault+'";'
          +'this.input_query_cancelcaption="'+cancelButtonCaption+'";'
          +'this.input_query_okcaption="'+confirmButtonCaption+'";'
          +'this.input_query_method="'+AMethedName+'";'
          +'this.input_query_handle='+IntToStr(AForm.Handle)+';'
          +'this.input_query_visible=true;';
     AForm.HelpFile := sMsgCode;
end;



end.
